﻿using System.Linq;
using System.Web.Mvc;
using EFDataModel;
using EFDataModel.Enums;

namespace ContactMvc4TB.Helpers.DynamicDataType
{
    /// <summary>
    /// Erstellt das passende Editor Control für den über jeweiligen EFDynamicDataType. (z.B. Inputfelder, checkboxen, ...) und füllt es mit den Daten die angezeigt werden sollen
    /// Außerdem wird für Unobstrusive Client Validation die passenden Attribute zum Input Control hinzugefügt. 
    /// bzw. wenn ein ServerValidation Error aufgetreten ist, wird dieser hier dem Control hinzugefügt und damit auf der Webseite richtig angezeigt.
    /// </summary>
    public class DynamicDataEditorControlCreator
    {
        #region Properties
        /// <summary>
        /// Der Aktuelle HTMLHelpder
        /// </summary>
        public HtmlHelper CurrentHtmlHelper { get; set; }

        /// <summary>
        /// Der Dynamische Datentyp um den es sich handelt der abgebildet werden soll
        /// </summary>
        public EFDynamicDataType CurrentDynamicDataType { get; set; }

        /// <summary>
        /// Der Text der angezeigt werden soll in der Textbox oder ausgewählt werden soll in der DDL
        /// </summary>
        public dynamic Value { get; set; }

        /// <summary>
        /// Der zugehörige Validierungsfehler, wenn ein Fehler aufgetreten ist bei der Eingabe der erst Serverseitig erkannt wurde, ist dieser hier enthalten
        /// </summary>
        public DynamicDataValidationError CurrentValidationError { get; set; }

        /// <summary>
        /// stellt das aktuelle Kontrol dar, was erstellt wird als String (HTML)
        /// </summary>
        private string CurrentControl { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Initialisieren der Klasse die das kontroll erstellt.
        /// </summary>
        /// <param name="htmlHelper">der Aktuelle HTML Helpder</param>
        /// <param name="efDynamicDataType">Der Datentyp der dargestellt werden soll</param>
        /// <param name="currentValidationError">Der aktuelle Validierungsfehler, der beim Auslesen der Daten aufgetreten ist.</param>
        /// <param name="value">Der value der angezeigt werden soll | wenn keiner angeben wurde Null übergeben!</param>
        public DynamicDataEditorControlCreator(HtmlHelper htmlHelper, EFDynamicDataType efDynamicDataType, dynamic value, DynamicDataValidationError currentValidationError)
        {
            CurrentHtmlHelper = htmlHelper;
            CurrentDynamicDataType = efDynamicDataType;
            Value = value;
            CurrentValidationError = currentValidationError;
            CurrentControl = string.Empty;
        }

        #endregion

        #region Public Functions
        /// <summary>
        /// Erstellt das passende HTML Control
        /// </summary>
        /// <returns></returns>
        public string CreateHtmlControl()
        {
            //Wenn das Control nicht angezeigt werden soll, dann wird auch kein Code erstellt.
            if (CurrentDynamicDataType.IsVisible)
            {
                //Als erstes den InputType erstellen
                AddInputType();

                switch (CurrentDynamicDataType.DynamicDataType)
                {
                    //Email ist wie String nur das ich hier bereits in der Oberfläche automatisch den RegEx Value setzte
                    case EFDynamicDataTypes.EMail:
                    case EFDynamicDataTypes.String:
                        AddDefaultValidationValues();
                        AddLengthValidationValues(EFDynamicDataTypes.String);
                        //Das Control abschließen
                        CurrentControl += " data-val=\"true\"></input>&nbsp;";
                        break;

                    case EFDynamicDataTypes.Textblock:
                        AddDefaultValidationValues();
                        AddLengthValidationValues(EFDynamicDataTypes.String);
                        //Der Anzeigewert der Textarea wird zwischen den textarea Tags angegeben
                        string currentValue = Value as string ?? string.Empty;
                        CurrentControl += string.Format(" data-val=\"true\">{0}</textarea>&nbsp;", currentValue);
                        break;

                    case EFDynamicDataTypes.Datetime:
                        AddDefaultValidationValues();
                        CurrentControl += " data-val=\"true\"></input>&nbsp;";
                        break;

                    case EFDynamicDataTypes.Integer:
                        AddDefaultValidationValues();
                        AddNumberValidationValues(EFDynamicDataTypes.Integer);
                        CurrentControl += " data-val=\"true\"></input>&nbsp;";
                        break;

                    case EFDynamicDataTypes.Decimal:
                        AddDefaultValidationValues();
                        CurrentControl += " data-val=\"true\"></input>&nbsp;";
                        break;

                    case EFDynamicDataTypes.Boolean:
                        //Für Boolean gibt es keine zusätzlichen Validierungsfelder
                        CurrentControl += " data-val=\"true\"></input>&nbsp;";
                        break;

                    case EFDynamicDataTypes.List:
                        AddDefaultValidationValues();
                        CurrentControl += "  data-val=\"true\">";
                        //In der Liste Selbst werden die einzelnen Items "options" angezeigt
                        AddListOptions();
                        CurrentControl += "</select>";
                        break;
                }

                //Die Anzeige für den Validationtext, wird an jedes Control erstellt.
                //Wenn kein externer Fehler aufgetreten ist
                if (CurrentValidationError == null)
                {
                    CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.ValidationTextRESX, CurrentDynamicDataType.CustomCssClass, CurrentDynamicDataType.HtmlName);
                }
                else
                {
                    //Eintragen der Fehlermeldung die aus der ErrorMessage Klasse kommt.
                    CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.PostBackValidationRESX, CurrentDynamicDataType.CustomCssClass, CurrentDynamicDataType.HtmlName, CurrentValidationError.ErrorMessage);
                }
            }

            return CurrentControl;
        }
        #endregion

        #region Private
        /// <summary>
        /// zur aktuellen Dropdownliste die passenden Options hinzufügen
        /// </summary>
        private void AddListOptions()
        {
            string currentValue = Value != null ? Value.ToString() : string.Empty;
            string options = "selected=\"selected\"";
            //Nullten Eintrag hinzufügen der das Bitte Wählen beinhaltet
            if (!string.IsNullOrEmpty(currentValue))
            {
                //Es gibt bereits einen Wert der ausgewählt werden soll, daher hier NICHT Selected eintragen
                CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.DropdownListOptionRESX, string.Empty, string.Empty,GlobalResources.GlobalStrings.PleaseSelectItemsRESX);
            }
            else
            {
                //Der "Bitte Wählen" Eintrag ist ausgewählt und soll angezeigt werden.
                CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.DropdownListOptionRESX, string.Empty, options,GlobalResources.GlobalStrings.PleaseSelectItemsRESX);
            }

            //Alle Optionsheraussuchen die für diese Dropdownliste zur Verfügung stehen und damit Alle Einträge für den aktuellen Dynamischen Datentyp durchgehen
            foreach (DynamicDataTypeListEntry eListEntry in CurrentDynamicDataType.DynamicDataTypeListEntries.OrderBy(p=>p.SortId))
            {
                //Dann handelt es sich um den aktuell ausgewählten Listeneintrag der angezeigt werden soll
                if (!string.IsNullOrEmpty(currentValue) && eListEntry.ID == Value)
                {
                    CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.DropdownListOptionRESX, eListEntry.ID, options, eListEntry.Name);
                }
                else
                {
                    //Ein normaler Eintrag wird angezeigt
                    CurrentControl += string.Format(GlobalResources.DynamicDataTypeTemplates.DropdownListOptionRESX, eListEntry.ID, string.Empty, eListEntry.Name);
                }
            }
        }

        /// <summary>
        /// Zum passenden Datentyp das passende Feld erstellen
        /// Es werden per JQuery nur Ganzzahlenwerte abgefangen, außerdem gibt es keine Datumsvalidation dies wird alles Serverseitig geprüft.
        /// </summary>
        private void AddInputType()
        {
            string currentValue = string.Empty;
            //Die Css Klasse für eine Valide Eingabe festlegen
            string validationCssClass = "valid";

            //Den Ausgabestring festlegen für eine valide Eingabe. Beim Ausgabestring handelt es sich um den Wert der beim Erstellen des Controls angezeigt werden soll.
            //Der Ausgabestring ist auch abhängig von dem jeweiligen Datentyp der ausgegeben werden soll
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Textblock || CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.String || CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.EMail)
            {
                currentValue = Value as string ?? string.Empty;
            }

            //Den Integerwert anpassen
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Integer)
            {
                if (Value != null)
                {
                    currentValue = Value.ToString();
                }
            }

            //Den Dezimalwert anpassen
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Decimal)
            {
                if (Value != null)
                {
                    currentValue = Value.ToString();
                }
            }

            //Das Ausgabedatumsformat anpassen
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Datetime)
            {
                if (Value != null)
                {
                    currentValue = Value.ToString(WebConstants.InputDateTimeFormat);
                }
            }

            //Wenn es sich um einen Boolean handelt steht im Value Checked
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Boolean)
            {
                if (Value != null && Value)
                {
                    currentValue = "checked";
                }
            }

            //Wenn ein Fehler aufgetreten ist, dann die alternativen Klassen und Werte setzten
            if (CurrentValidationError != null)
            {
                //Error Css Klasse setzten bei einem Validierungsfehler
                validationCssClass = "input-validation-error";
                //Wieder den ungültigen Wert einsetzten, damit dieser wieder angezeigt und korrigiert werden kann.
                currentValue = CurrentValidationError.OriginalValue ?? string.Empty;
            }

            //Abhängig vom Datentyp das passende Eingabefeld erstellen
            switch (CurrentDynamicDataType.DynamicDataType)
            {
                //Für alle folgenden Datentypen wird eine Textbox als Eingabemedium erstellt.
                case EFDynamicDataTypes.String:
                case EFDynamicDataTypes.Decimal:
                case EFDynamicDataTypes.Integer:
                case EFDynamicDataTypes.EMail:
                    //Rendern des passenden Controls
                    CurrentControl = string.Format("<input id=\"{0}\" type=\"text\" value=\"{1}\" name=\"{0}\" class=\"text-box single-line {2} {3}\"",
                                                   CurrentDynamicDataType.HtmlName, currentValue, validationCssClass, CurrentDynamicDataType.CustomCssClass);
                    break;

                case EFDynamicDataTypes.Datetime:
                    //für das Datum muss eine extra CssKlasse eingefügt werden für den DatePicker, damit dieser weiß auf welches Controll dieser angewendet werden soll.
                    //Initialisiert wird der Datepicker in der _Layout.cshtml Datei.
                    CurrentControl = string.Format("<input id=\"{0}\" type=\"text\" value=\"{1}\" name=\"{0}\" class=\"text-box single-line {4} {2} {3}\"",
                                                   CurrentDynamicDataType.HtmlName, currentValue, WebConstants.DatePickerCssClass, CurrentDynamicDataType.CustomCssClass, validationCssClass);
                    break;

                case EFDynamicDataTypes.Boolean:
                    //Erstellen der Checkbox für den Boolean Typ
                    CurrentControl = string.Format("<input id=\"{0}\" class=\"check-box {1} {2}\" type=\"checkbox\" value=\"{3}\" {4} name=\"{0}\"",
                        CurrentDynamicDataType.HtmlName, validationCssClass, CurrentDynamicDataType.CustomCssClass, currentValue == "checked" ? "true" : "false", currentValue);
                    break;

                case EFDynamicDataTypes.Textblock:
                    //Der anzeigewert für den Textblock liegt vor dem EndeTag von Textarea und wird daher nicht hier eingefügt
                    CurrentControl = string.Format("<textarea id=\"{0}\" class=\"text-box multi-line {1} {2}\" name=\"{0}\"", CurrentDynamicDataType.HtmlName, validationCssClass, CurrentDynamicDataType.CustomCssClass);
                    break;

                case EFDynamicDataTypes.List:
                    //Bei einer Liste wird der aktuell angezeigte Wert in den Options eingestellt.
                    CurrentControl = string.Format("<select id=\"{0}\" class=\"{1} {2}\" name=\"{0}\"", CurrentDynamicDataType.HtmlName, validationCssClass, CurrentDynamicDataType.CustomCssClass);
                    break;
            }
        }

        /// <summary>
        /// Die passenden Validierungsstrings hinzufügen für den Typ Eingabefeld (string) für Required und RegEx
        /// </summary>
        private void AddDefaultValidationValues()
        {
            //Wenn das Eingabefeld benötigt wird
            if (CurrentDynamicDataType.IsRequired)
            {
                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.FieldIsRequiredRESX, CurrentDynamicDataType.Name);
            }

            //Wenn ein Regext String angegeben wurden diesen einsetzten.
            if (CurrentDynamicDataType.RegExString.Trim().Length > 0)
            {
                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.RegexPatternRESX, CurrentDynamicDataType.RegExString, CurrentDynamicDataType.Name);
            }
        }

        /// <summary>
        /// Validationvalues für Zahlen hinzufügen
        /// </summary>
        private void AddNumberValidationValues(EFDynamicDataTypes efDynamicDataType)
        {
            //Wenn es sich um einen Integerwert handelt, wird dieser Validator noch zusätzlich hinzugefügt, funktioniert nur bei GANZEN Zahlen!
            if (CurrentDynamicDataType.DynamicDataType == EFDynamicDataTypes.Integer)
            {
                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.IntegerValueRESX, CurrentDynamicDataType.Name);
            }
        }

        /// <summary>
        /// ValidationValues für die Stringlänge hinzufügen, hier muss zwischen den Datentypen unterschieden werden wie String und Integer. Datetime muss hier nicht behandelt werden
        /// da dies nicht per JavaScript geprüft wird, sondern nur im Postback ob das Datum auch valide ist.
        /// </summary>
        /// <param name="efDynamicDataType">Der DatenTyp für den die Lengs berechnet werden soll.</param>
        private void AddLengthValidationValues(EFDynamicDataTypes efDynamicDataType)
        {
            //Es wird nur nach einer maximal Länge verlangt
            if (CurrentDynamicDataType.MaxLength.HasValue && !CurrentDynamicDataType.MinLength.HasValue)
            {
                string errorMessage = string.Empty;
                if (efDynamicDataType == EFDynamicDataTypes.String)
                {
                    errorMessage = string.Format(GlobalResources.SystemMessages.StringMaxLengthRESX, CurrentDynamicDataType.Name, CurrentDynamicDataType.MaxLength);
                }

                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.MaxLengthRESX, CurrentDynamicDataType.MaxLength, errorMessage);
            }

            //Es wird nur nach einer Min Länge verlangt
            if (!CurrentDynamicDataType.MaxLength.HasValue && CurrentDynamicDataType.MinLength.HasValue)
            {
                string errorMessage = string.Empty;
                if (efDynamicDataType == EFDynamicDataTypes.String)
                {
                    errorMessage = string.Format(GlobalResources.SystemMessages.StringMinLengthRESX, CurrentDynamicDataType.Name, CurrentDynamicDataType.MinLength);
                }

                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.MinLengthRESX, CurrentDynamicDataType.MinLength, errorMessage);
            }

            //Es wird eine minimale und eine Maximale Länge verlangt.
            if (CurrentDynamicDataType.MaxLength.HasValue && CurrentDynamicDataType.MinLength.HasValue)
            {
                string errorMessage = string.Empty;
                if (efDynamicDataType == EFDynamicDataTypes.String)
                {
                    errorMessage = string.Format(GlobalResources.SystemMessages.StringMinMaxLengthRESX, CurrentDynamicDataType.Name, CurrentDynamicDataType.MinLength, CurrentDynamicDataType.MaxLength);
                }

                CurrentControl += " " + string.Format(GlobalResources.DynamicDataTypeTemplates.MinAndMaxLengthRESX, CurrentDynamicDataType.MinLength, CurrentDynamicDataType.MaxLength, errorMessage);
            }
        }
        #endregion

        //Demodaten wie ein Input feld vom Typ Text ausssehen kann, mit allen Validatorwerten
        //<input id="UserName" type="text" value="jr@gmx.de" name="UserName" class="text-box single-line valid"  
        //data-val-required="Das Feld 'Email' ist erforderlich." 
        //data-val-regex-pattern="^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-zA-Z0-9]{1}[a-zA-Z0-9\-]{0,62}[a-zA-Z0-9]{1})|[a-zA-Z])\.)+[a-zA-Z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$"
        //data-val-regex="Bitte eine gültige Emailadresse eingeben." 
        //data-val-length-min="2" data-val-length-max="100" 
        //data-val-length="Das Feld 'Email' muss eine Zeichenfolge mit der Mindestlänge 2 und der Höchstlänge 100 sein." 
        //data-val-number="Das Feld 'Minimale Länge' muss eine Zahl sein."
        //data-val="true">

        //<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="UserName"></span>

        //Demodaten für einen Fehler der beim Postback erstellt wurde für ein Inputfeld ohne JavaScript
        //<input id="MaxLength" class="input-validation-error text-box single-line" type="text" value="aasdad" name="MaxLength">
        //<span class="field-validation-error" data-valmsg-replace="true" data-valmsg-for="MaxLength">Der Wert "aasdad" ist für "Maximale Länge" nicht gültig.</span>

        //Demodaten für ein Mehrzeiliges Textfeld
        //<textarea id="Message" class="text-box multi-line input-validation-error" name="Message" data-val-required="Das Feld &quot;Nachricht&quot; ist erforderlich." data-val="true"></textarea>

        //Demodaten für eine Checkbox
        //<input id="IsRequired" class="check-box" type="checkbox" value="true" name="IsRequired" data-val-required="Das Feld &quot;Eingabe erforderlich&quot; ist erforderlich." data-val="true">

        //Demodaten für eine Dropdownliste
        //<select id="UserTitle" class="valid" name="UserTitle" data-val-required="Bitte eine Anrede auswählen." data-val="true">
        //<option value="">-- Bitte wählen --</option>
        //<option value="7" selected="selected">Mann</option>
        //<option value="8">Frau</option>
        //<option value="9">Familie</option>
        //</select>
        //<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="UserTitle"></span>
    }
}